﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Reflection;
using GoodStuff.Text;

namespace GoodStuff.Diagnostics
{
    /// <summary>
    /// HttpHandler to show the most-recent messages in the Sql Log.
    /// </summary>
    /// <remarks>
    /// &lt;add verb="GET" path="Exceptions.axd" type="GoodStuff.Diagnostics.ExceptionHandlerSqlViewer, GoodStuff"/&gt;
    /// </remarks>
    public class ExceptionHandlerSqlViewer : IHttpHandler
    {
        public bool IsReusable
        {
            get { return true; }
        }

            private string ConnectionName
        {
            get
            {
                //copied from ExceptionHandlerModule

                ExceptionHandlerConfiguration configuration = ExceptionHandlerConfigurationSectionHandler.GetConfiguration("goodStuff/exceptionHandlerConfiguration");
                if (configuration != null && configuration.Enabled)
                {
                    if (configuration.Sql != null)
                    {
                        return configuration.Sql.ConnectionName;
                    }
                }
                throw new Exception("Sql Exception logging is not enabled");
            }
        }

        public void ProcessRequest(HttpContext context)
        {
            HtmlTextWriter writer = new HtmlTextWriter(context.Response.Output);

            BeginRender(writer);

            writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing, "0");
            writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "0");
            writer.AddAttribute(HtmlTextWriterAttribute.Border, "0");
            writer.AddStyleAttribute(HtmlTextWriterStyle.Width, "100%");
            writer.RenderBeginTag(HtmlTextWriterTag.Table);

            Guid details = GoodStuff.Web.QueryStringTools.GetParameter<Guid>(context.Request, "details", false, Guid.Empty);
            if (details == Guid.Empty)
            {

                int numberOfRows = GoodStuff.Web.QueryStringTools.GetParameter<int>(context.Request, "rows", false, 50);
                int startAt = GoodStuff.Web.QueryStringTools.GetParameter<int>(context.Request, "startat", false, 0);

                using (var database = new Data.DataContextBuilder(ConnectionName).Build())
                {

                    var logEntries = database.Select<ExceptionHandlerSql.LogEntry>().OrderByDescending(p => p.Date).Skip(startAt).Take(numberOfRows);
                    RenderRows(writer, numberOfRows, startAt, logEntries);
                    writer.RenderEndTag();
                    writer.AddAttribute(HtmlTextWriterAttribute.Cellspacing, "0");
                    writer.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "0");
                    writer.AddAttribute(HtmlTextWriterAttribute.Border, "0");
                    writer.RenderBeginTag(HtmlTextWriterTag.Table);

                    writer.RenderBeginTag(HtmlTextWriterTag.Tr);
                    writer.AddAttribute(HtmlTextWriterAttribute.Colspan, "8");
                    writer.AddAttribute(HtmlTextWriterAttribute.Class, "alt");
                    RenderHead(writer, "Last month exceptions");
                    writer.RenderEndTag();

                    writer.AddAttribute(HtmlTextWriterAttribute.Class, "subhead");            
                    writer.RenderBeginTag(HtmlTextWriterTag.Tr);
                    RenderHead(writer, "Date");
                    RenderHead(writer, "#Entries");
                    RenderHead(writer, "");
                    RenderHead(writer, "% of Total");
                    writer.RenderEndTag();

                    //render a statistics table.
                    var logEntriesAll = database.Select<ExceptionHandlerSql.LogEntry>().Where(p => p.Date > DateTime.Now.AddMonths(-1));
                    var groupByDate = logEntriesAll.GroupBy(p => p.Date.Date).Select(q => new { Key = q.Key, Count = q.Count() });
                    if (groupByDate.Count() > 0) //avoid error in .Max LINQ without items
                    {
                        var max = groupByDate.Max(p => p.Count);
                        bool alt = false;
                        foreach (var counter in groupByDate.OrderByDescending(p => p.Key))
                        {
                            if (alt)
                            {
                                writer.AddAttribute(HtmlTextWriterAttribute.Class, "alt");
                            }
                            writer.RenderBeginTag(HtmlTextWriterTag.Tr);
                            RenderColumn(writer, counter.Key.ToShortDateString());
                            RenderColumn(writer, counter.Count.ToString());

                            string div = string.Format("<div style=\"width: 400px\"><span style=\"margin: 2px; width: {0}%; background: gray; border: 1px solid transparent;%\"></span></div>", counter.Count * 100 / max);


                            RenderColumnNoEncode(writer, div);
                            RenderColumn(writer, string.Format("{0:0}%", (float)counter.Count * 100 / logEntriesAll.Count()));
                            writer.RenderEndTag();
                            alt = !alt;
                        }
                    }
                }               
            }
            else
            {             
                writer.RenderBeginTag(HtmlTextWriterTag.Tr);
                writer.AddAttribute(HtmlTextWriterAttribute.Colspan, "8");
                writer.AddAttribute(HtmlTextWriterAttribute.Class, "alt");
                RenderHead(writer, "Exception details");
                writer.RenderEndTag();
            
                RenderDetails(new TableWriter(writer), details);
            }

            writer.RenderEndTag();
            EndRender(writer);
        }

        private void RenderDetails(TableWriter writer, Guid details)
        {
           using (var database = new Data.DataContextBuilder(ConnectionName).Build())
            {
                var logEntry = database.Single<ExceptionHandlerSql.LogEntry>(p => p.ID == details);

                writer.RenderRow("ID", logEntry.ID.ToString());
                writer.RenderRow("Date", logEntry.Date.ToString());
                writer.RenderRow("Message", logEntry.Message, "err");
                writer.RenderRow("Application", logEntry.Application);
                writer.RenderRow("Url", logEntry.Url);
                writer.RenderRow("QueryString", logEntry.QueryString);
                writer.RenderRow("HostHeader", logEntry.HostHeader);
                writer.RenderRow("Module", logEntry.Module);
                writer.RenderRow("RemoteID", logEntry.RemoteIP);
                writer.RenderRow("Severity", logEntry.Severity.ToString());
                writer.RenderRow("StackTrace", logEntry.StackTrace.Replace("\n", "<BR/>"));                
            }
        }


        private void RenderRows(HtmlTextWriter writer, int numberOfRows, int startAt, IEnumerable<GoodStuff.Diagnostics.ExceptionHandlerSql.LogEntry> logEnties)
        {
            writer.AddAttribute(HtmlTextWriterAttribute.Href, "?startAt=0");
            writer.RenderBeginTag(HtmlTextWriterTag.A);
            writer.Write("most recent");
            writer.RenderEndTag();

            writer.Write(" ");

            writer.AddAttribute(HtmlTextWriterAttribute.Href, "?startAt=" + (startAt + numberOfRows) + "&rows=" + numberOfRows);
            writer.RenderBeginTag(HtmlTextWriterTag.A);
            writer.Write("show next ");
            writer.Write(numberOfRows);
            writer.RenderEndTag();

            writer.RenderBeginTag(HtmlTextWriterTag.Tr);
            writer.AddAttribute(HtmlTextWriterAttribute.Colspan, "8");
            writer.AddAttribute(HtmlTextWriterAttribute.Class, "alt");
            RenderHead(writer, "Most recent exceptions");
            writer.RenderEndTag();

            writer.AddAttribute(HtmlTextWriterAttribute.Class, "subhead");
            writer.RenderBeginTag(HtmlTextWriterTag.Tr);
            RenderHead(writer, "Date/Time");
            RenderHead(writer, "Application");
            RenderHead(writer, "Url");
            RenderHead(writer, "Message");
            RenderHead(writer, "RemoteIP");
            RenderHead(writer, "&nbsp;");
            writer.RenderEndTag();

            bool alt = false;

            
            foreach (var logEntry in logEnties)
            {
                if (alt)
                {
                    writer.AddAttribute(HtmlTextWriterAttribute.Class, "alt");
                }
                writer.RenderBeginTag(HtmlTextWriterTag.Tr);

                RenderColumn(writer, logEntry.Date.ToShortDateString() + " " + logEntry.Date.ToLongTimeString());
                RenderColumn(writer, logEntry.Application);
                RenderColumn(writer, logEntry.Url.Limit(50));
                writer.AddAttribute(HtmlTextWriterAttribute.Class, "long");
                RenderColumn(writer, logEntry.Message.Limit(80));
                RenderColumn(writer, logEntry.RemoteIP);

                writer.RenderBeginTag(HtmlTextWriterTag.Td);
                writer.AddAttribute(HtmlTextWriterAttribute.Href, "?details=" + logEntry.ID.ToString());
                writer.RenderBeginTag(HtmlTextWriterTag.A);
                writer.Write("View Details");
                writer.RenderEndTag();
                writer.RenderEndTag();
                writer.RenderEndTag();

                alt = !alt;
            }
        }

        private void RenderHead(HtmlTextWriter writer, string value)
        {
            writer.RenderBeginTag(HtmlTextWriterTag.Th);
            writer.Write(value);
            writer.RenderEndTag();
        }


        private void RenderColumn(HtmlTextWriter writer, string value)
        {
            writer.AddAttribute(HtmlTextWriterAttribute.Nowrap, "true");
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            writer.Write(HttpUtility.HtmlEncode(value));
            writer.RenderEndTag();
        }

        private void RenderColumnNoEncode(HtmlTextWriter writer, string value)
        {
            writer.AddAttribute(HtmlTextWriterAttribute.Nowrap, "true");
            writer.RenderBeginTag(HtmlTextWriterTag.Td);
            writer.Write(value);
            writer.RenderEndTag();
        }
        
        private void BeginRender(HtmlTextWriter writer)
        {
            writer.RenderBeginTag(HtmlTextWriterTag.Style);
            writer.Write(@"
TABLE TR TD { vertical-align: top; }
b { color:white }
body { background-color:white; color:black;font: 10pt verdana, arial; }
table { clear:left; font: 10pt verdana, arial; cellspacing:0; cellpadding:0; margin-bottom:25; width: 100%;}
tr.subhead { background-color:#cccccc;}
th { padding:0,3,0,3; text-align: left; }
th.alt { background-color:black; color:white; padding:3,3,2,3; }
td { color: black; padding:0,3,3,3; text-align: left }
td.err { color: red; }
td.label { font-weight: bold; }
tr.alt { background-color:#eeeeee }
h1 { font: 24pt verdana, arial; margin:0,0,0,0}
h2 { font: 18pt verdana, arial; margin:0,0,0,0}
h3 { font: 12pt verdana, arial; margin:0,0,0,0}
th a { color:darkblue; font: 8pt verdana, arial; }
a { color:darkblue;text-decoration:none }
a:hover { color:darkblue;text-decoration:underline; }
div.outer { width:90%; margin:15,15,15,15}
table.viewmenu td { background-color:#006699; color:white; padding:0,5,0,5; }
table.viewmenu td.end { padding:0,0,0,0; }
table.viewmenu a {color:white; font: 8pt verdana, arial; }
table.viewmenu a:hover {color:white; font: 8pt verdana, arial; }
a.tinylink {color:darkblue; background-color:black; font: 8pt verdana, arial;text-decoration:underline;}
a.link {color:darkblue; text-decoration:underline;}
div.buffer {padding-top:7; padding-bottom:17;}
.small { font: 8pt verdana, arial }
.long { overflow: hidden; }
table td { padding-right:20 }
table td.nopad { padding-right:5 }
");

            writer.RenderEndTag();

            writer.RenderBeginTag(HtmlTextWriterTag.Body);
            writer.RenderBeginTag(HtmlTextWriterTag.H1);
            writer.Write("Exception Log");
            writer.RenderEndTag();

            writer.RenderBeginTag(HtmlTextWriterTag.H2);
            writer.Write("Messages were written to the database log");
            writer.RenderEndTag();

            writer.WriteBreak();
        }

        private void EndRender(HtmlTextWriter writer)
        {  
            writer.RenderBeginTag(HtmlTextWriterTag.Hr);
            writer.RenderEndTag();
            writer.AddAttribute(HtmlTextWriterAttribute.Class, "small");
            writer.RenderBeginTag(HtmlTextWriterTag.Span);
            string version = GoodStuff.Web.Controls.VersionLabelText.GetDisplayText(Assembly.GetExecutingAssembly(), Web.Controls.VersionInformation.AssemblyVersion);
            writer.RenderEndTag();
            writer.Write(version);

            writer.RenderEndTag();  //BODY
        }
    

        private class TableWriter 
        {
            private HtmlTextWriter _writer;
            private bool _alt;

            public TableWriter(HtmlTextWriter writer)
            {
                _writer = writer;
            }

            public void RenderRow(string label, string value)
            {
                RenderRow(label, value, null);
            }

            public void RenderRow(string label, string value, string labelClass)
            {
                if (_alt)
                {
                    _writer.AddAttribute(HtmlTextWriterAttribute.Class, "alt");
                }

                _writer.RenderBeginTag(HtmlTextWriterTag.Tr);
                _writer.AddAttribute(HtmlTextWriterAttribute.Class, "label");
                _writer.RenderBeginTag(HtmlTextWriterTag.Td);
                _writer.Write(label);
                _writer.RenderEndTag();
                if (labelClass != null)
                {
                    _writer.AddAttribute(HtmlTextWriterAttribute.Class, labelClass);
                }
                _writer.RenderBeginTag(HtmlTextWriterTag.Td);
                _writer.Write(value);
                _writer.RenderEndTag();
                _writer.RenderEndTag();

                _alt = !_alt;
            }
        }
    }
}
